#include "stdafx.h"
#include "ipgeodll.h"

#define wszURL					L"http://maps.live.com/"
#define szLAT_MARKER			"window.revIPLat = '"
#define szLONG_MARKER			"window.revIPLong = '"

//
// Heap helpers
//

LPVOID 
WINAPI
Alloc(SIZE_T cb)
{
    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
}

void 
WINAPI
Free(LPVOID pv)
{
    HeapFree(GetProcessHeap(), 0, pv);
}

//
// Flow macros
//

static 
void
WINAPI
_OutputDbgStr(
    __in  LPSTR szMsg,
    __in  DWORD dwStatus)
{
    CHAR rgsz [256];

    StringCbPrintfA(
        rgsz,
        sizeof(rgsz),
        "ERROR: %s - 0x%x\n",
        szMsg,
        dwStatus);
    OutputDebugStringA(rgsz);
}

#define CHECK_DWORD(_X) {                                             \
    if (ERROR_SUCCESS != (status = _X)) {                           \
        _OutputDbgStr(#_X, status);                                 \
        __leave;                                                    \
    }                                                               \
}

#define CHECK_BOOL(_X) {                                              \
    if (FALSE == (_X)) {                                            \
        status = GetLastError();                                    \
        _OutputDbgStr(#_X, status);                                 \
        __leave;                                                    \
    }                                                               \
}

#define CHECK_PTR(_X) {                                               \
    if (NULL == (_X)) {                                             \
        status = GetLastError();                                    \
        _OutputDbgStr(#_X, status);                                 \
        __leave;                                                    \
    }                                                               \
}

#define CHECK_ALLOC(_X) {                                             \
    if (NULL == (_X)) {                                             \
        status = ERROR_NOT_ENOUGH_MEMORY;                           \
        __leave;                                                    \
    }                                                               \
}

//
// Create an HTTP GET request and attempt to send it using the specified
// security requirement.
// 
DWORD
WINAPI
_CreateAndSendGetRequest(
    __in    HINTERNET hConnect,
    __in    BOOL fTls,
    __out   HINTERNET *phRequest)
{
    DWORD status = 0;

    __try
    {
        CHECK_PTR(*phRequest = WinHttpOpenRequest( 
            hConnect, 
            L"GET", 
            NULL,
            NULL, 
            WINHTTP_NO_REFERER, 
            WINHTTP_DEFAULT_ACCEPT_TYPES, 
            fTls ? WINHTTP_FLAG_SECURE : 0));

        CHECK_BOOL(WinHttpSendRequest( 
            *phRequest,
			WINHTTP_NO_ADDITIONAL_HEADERS,
            0,
            WINHTTP_NO_REQUEST_DATA, 
            0, 
            0, 
            0));
    }
    __finally
    {
    }

    return status;
}

BOOL
WINAPI
_ParseCommon(
	__in_bcount(cbBuffer)		LPSTR szBuffer,
	__in						DWORD cbBuffer,
	__in						LPSTR szMarker,
	__out						float *pflt)
{
	LPSTR szMarkerLocation = NULL;

	UNREFERENCED_PARAMETER(cbBuffer);

	if (NULL == (szMarkerLocation = StrStrIA(szBuffer, szMarker)))
		return FALSE;

	szMarkerLocation += strlen(szMarker);
	if (1 != sscanf_s(szMarkerLocation, "%f", pflt))
		return FALSE;

	return TRUE;
}

BOOL
WINAPI
_ParseForLatitude(
	__in_bcount(cbBuffer)		LPSTR szBuffer,
	__in						DWORD cbBuffer,
	__out						float *pfltLat)
{
	return _ParseCommon(szBuffer, cbBuffer, szLAT_MARKER, pfltLat);
}

BOOL
WINAPI
_ParseForLongitude(
	__in_bcount(cbBuffer)		LPSTR szBuffer,
	__in						DWORD cbBuffer,
	__out						float *pfltLong)
{
	return _ParseCommon(szBuffer, cbBuffer, szLONG_MARKER, pfltLong);
}

//
// Retrieve the rest of the HTTP response data from the server
// 
DWORD
WINAPI
_GetResponse(
    __in        HINTERNET hRequest,
	__out		float *pfltLat,
	__out		PBOOL pfFoundLat,
	__out		float *pfltLong,
	__out		PBOOL pfFoundLong)
{
    DWORD status = 0;
    PBYTE pbData = NULL;
    DWORD cbData = 0;
    DWORD cbRead = 0;

    __try
    {
        do 
        {
            cbData = 0;
            CHECK_BOOL(WinHttpQueryDataAvailable(hRequest, &cbData));

            CHECK_ALLOC(pbData = (PBYTE) Alloc(cbData));

            CHECK_BOOL(WinHttpReadData(hRequest, pbData, cbData, &cbRead));
			
			if (FALSE == *pfFoundLat)
				*pfFoundLat = _ParseForLatitude((LPSTR) pbData, cbData, pfltLat);

			if (FALSE == *pfFoundLong)
				*pfFoundLong = _ParseForLongitude((LPSTR) pbData, cbData, pfltLong);
            
			Free(pbData);
            pbData = NULL;
        } while (cbData > 0 && (FALSE == *pfFoundLat || FALSE == *pfFoundLong));
    }
    __finally
    {
        if (NULL != pbData)
            Free(pbData);
    }

    return status;
}

DWORD
WINAPI
_GetLatAndLong(
	__out		float *pfltLat,
	__out		PBOOL pfFoundLat,
	__out		float *pfltLong,
	__out		PBOOL pfFoundLong)
{
	DWORD status = 0;
	URL_COMPONENTS UrlComponents = {0};
    WCHAR rgwszHostName [MAX_PATH];
    HINTERNET hSession = NULL, 
           hConnect = NULL,
           hRequest = NULL;

	__try
	{
        //
        // Crack the URL
        // 

        UrlComponents.dwStructSize = sizeof(UrlComponents);
        UrlComponents.lpszHostName = rgwszHostName;
        UrlComponents.dwHostNameLength = 
            sizeof(rgwszHostName) / sizeof(rgwszHostName [0]);
        CHECK_BOOL(WinHttpCrackUrl(
            wszURL,
            0,
            0,
            &UrlComponents));

		//
        // Connect to the server
        // 
		// Windows Live will complain if the UserAgent header references an unknown
		// browser.
		//

        CHECK_PTR(hSession = WinHttpOpen( 
            L"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
            WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
            WINHTTP_NO_PROXY_NAME, 
            WINHTTP_NO_PROXY_BYPASS, 
            0));

        CHECK_PTR(hConnect = WinHttpConnect( 
            hSession, 
            rgwszHostName,
            INTERNET_DEFAULT_PORT,
            0));

		//
        // Issue the GET request 
        // 

        CHECK_DWORD(_CreateAndSendGetRequest(hConnect, FALSE, &hRequest));

        CHECK_BOOL(WinHttpReceiveResponse(hRequest, NULL));

        CHECK_DWORD(_GetResponse(
			hRequest, pfltLat, pfFoundLat, pfltLong, pfFoundLong));
	}
	__finally
	{
		if (hRequest) 
            WinHttpCloseHandle(hRequest);
        if (hConnect) 
            WinHttpCloseHandle(hConnect);
        if (hSession) 
            WinHttpCloseHandle(hSession);
	}

	return status;
}

//
// Export routines
//

DWORD
WINAPI
IPGeoGetLatitude(
	__out				float *pfltLat,
	__out				PBOOL pfFoundLat)
{
	float flt = 0;
	BOOL b = FALSE;

	return _GetLatAndLong(pfltLat, pfFoundLat, &flt, &b);
}

DWORD
WINAPI
IPGeoGetLongitude(
	__out				float *pfltLong,
	__out				PBOOL pfFoundLong)
{
	float flt = 0;
	BOOL b = FALSE;

	return _GetLatAndLong(&flt, &b, pfltLong, pfFoundLong);
}



